package mesweb.mesweb.controller.produce;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.farvision.common.api.CommonResult;
import com.farvision.common.exception.ApiException;
import com.hszh.wms.mbg.PermissionManager.entity.UmsAdmin;
import com.power.common.util.StringUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import mesweb.mesweb.dto.ProducePlan.DayReportDetailConditionDto;
import mesweb.mesweb.dto.ProducePlan.MonthGanttDto;
import mesweb.mesweb.dto.ProducePlan.ProduceLineBottleneckDto;
import mesweb.mesweb.entity.Procedure.MesProcedure;
import mesweb.mesweb.entity.pm.PMProduct;
import mesweb.mesweb.entity.produce.ProduceDayReport;
import mesweb.mesweb.entity.produce.ProduceOrderProductPlan;
import mesweb.mesweb.entity.produceLine.ModelProduceLine;
import mesweb.mesweb.entity.supply.sdMaterialRequire;
import mesweb.mesweb.enums.PlanMethod;
import mesweb.mesweb.enums.enumPlanSort;
import mesweb.mesweb.enums.enumScheduleState;
import mesweb.mesweb.service.UmsAdminService;
import mesweb.mesweb.service.pm.IPMProductService;
import mesweb.mesweb.service.procedure.IMesProductProcedureBranchProcedureService;
import mesweb.mesweb.service.produce.IProduceDayReportService;
import mesweb.mesweb.service.produce.IProduceOrderProductPlanService;
import mesweb.mesweb.service.produceLine.IModelProduceLineService;
import mesweb.mesweb.service.supply.IsdMaterialRequireService;
import mesweb.mesweb.vo.ProducePlan.MonthGantTask;
import mesweb.mesweb.vo.produceLine.ScheduleProduceLineResult;
import mesweb.mesweb.vo.produceLine.ScheduleProduceLineVo;
import mesweb.mesweb.vo.produceLine.SimpleProduceLineVo;
import mesweb.mesweb.vo.supply.MaterialRequiredVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * @Description: 订单产品排产
 * @Author: jeecg-boot
 * @Date: 2021-09-01
 * @Version: V1.0
 */

@Api(tags = "订单产品排产")
@RestController
@RequestMapping("/mes/ProduceOrderProductPlan")
public class ProduceOrderProductPlanController {
    @Autowired
    private IProduceOrderProductPlanService produceOrderProductPlanService;

    @Autowired
    private IModelProduceLineService produceLineService;

    @Autowired
    private IMesProductProcedureBranchProcedureService mesProductProcedureBranchProcedureService;

    @Autowired
    private IProduceDayReportService produceDayReportService;

    @Autowired
    private IPMProductService productService;

    @Autowired
    private IsdMaterialRequireService sdMaterialRequireService;

    @Autowired
    private UmsAdminService adminService;

    @GetMapping("/GetAll")
    @ApiOperation("获取全部数据列表")
    public CommonResult<?> GetAll(@ApiParam("名称") @RequestParam(value = "name", required = false) String name) {
        List<ProduceOrderProductPlan> data = new ArrayList<>();
        if (StringUtils.isBlank(name) || name.equals("全部")) {
            data = produceOrderProductPlanService.list();
        } else {
            QueryWrapper<ProduceOrderProductPlan> queryWrapper = new QueryWrapper<>();
            data = produceOrderProductPlanService.list(queryWrapper);
        }
        return CommonResult.success(data);
    }

    /**
     * 分页列表查询
     *
     * @param page
     * @param size
     * @param req
     * @return
     */
    @ApiOperation(value = "订单产品排产-分页列表查询", notes = "订单产品排产-分页列表查询")
    @GetMapping(value = "/list")
    public CommonResult<?> queryPageList(
                                         @RequestParam(name = "produceLineId", required = false,defaultValue = "0") Long produceLineId,
                                         @RequestParam(name = "planPeriod", required = false,defaultValue = "") String planPeriod,
                                         @RequestParam(name = "page", required = false,defaultValue = "1") Integer page,
                                         @RequestParam(name = "size", required = false,defaultValue = "10") Integer size,
                                         HttpServletRequest req) {
        QueryWrapper<ProduceOrderProductPlan> queryWrapper = new QueryWrapper<>();
        if (produceLineId > 0) {
            queryWrapper.eq("produce_line_id", produceLineId);
        }
        if(StrUtil.isNotBlank(planPeriod)){
            queryWrapper.eq("schedule_period", planPeriod);
        }
        Page<ProduceOrderProductPlan> pageProduceOrderProductPlan = new Page<ProduceOrderProductPlan>(page, size);
        IPage<ProduceOrderProductPlan> pageList = produceOrderProductPlanService.getProduceOrderProductPlanList(pageProduceOrderProductPlan, queryWrapper);
        return CommonResult.success(pageList);
    }

    /**
     * 编辑
     *
     * @param produceOrderProductPlan
     * @return
     */
    @ApiOperation(value = "订单产品排产-编辑", notes = "订单产品排产-编辑")
    @PostMapping(value = "/save")
    @Transactional(rollbackFor = Throwable.class)
    public CommonResult<?> edit(@RequestBody ProduceOrderProductPlan produceOrderProductPlan) {
        boolean result = false;
        if (null == produceOrderProductPlan.getScheduleState()) {
            produceOrderProductPlan.setScheduleState(enumScheduleState.UNSCHEDULE);
        }
        if (produceOrderProductPlan.getId() == 0L) {
            produceOrderProductPlanService.saveProduceOrderProductPlan(produceOrderProductPlan);
            result = true;
        } else {
            result = produceOrderProductPlanService.updateById(produceOrderProductPlan);
        }
        return CommonResult.success(result);
    }

    /**
     * 通过id删除
     *
     * @param id
     * @return
     */
    @ApiOperation(value = "订单产品排产-通过id删除", notes = "订单产品排产-通过id删除")
    @DeleteMapping(value = "/delete")
    public CommonResult<?> delete(@RequestParam(name = "id", required = true) String id) {
        produceOrderProductPlanService.removeById(id);
        return CommonResult.success("删除成功!");
    }

    /**
     * 批量删除
     *
     * @param ids
     * @return
     */
    @ApiOperation(value = "订单产品排产-批量删除", notes = "订单产品排产-批量删除")
    @DeleteMapping(value = "/deleteBatch")
    public CommonResult<?> deleteBatch(@ApiParam("ProduceOrderProductPlanIDs") @RequestBody List<Long> ids) {
        this.produceOrderProductPlanService.removeByIds(ids);
        return CommonResult.success("批量删除成功！");
    }

    /**
     * 通过id查询
     *
     * @param id
     * @return
     */
    @ApiOperation(value = "订单产品排产-通过id查询", notes = "订单产品排产-通过id查询")
    @GetMapping(value = "/queryById")
    public CommonResult<?> queryById(@RequestParam(name = "id", required = true) String id) {
        ProduceOrderProductPlan ProduceOrderProductPlan = produceOrderProductPlanService.getById(id);
        return CommonResult.success(ProduceOrderProductPlan);
    }

    /**
     * 获取所有的未排产的产线
     *
     * @return
     */
    @ApiOperation(value = "订单产品排产-获取未排产产线", notes = "订单产品排产-获取未排产产线")
    @GetMapping(value = "/getUnSceduledProduceLines")
    public CommonResult<?> getUnSceduledProduceLines(@RequestParam(name = "produceLineNo", required = false, defaultValue = "") String produceLineNo) {
        QueryWrapper<SimpleProduceLineVo> queryWrapper = new QueryWrapper<>();
        if (StringUtil.isNotEmpty(produceLineNo)) {
            queryWrapper.like("popp.produce_Line_No", produceLineNo);
        }
        List<SimpleProduceLineVo> lst = produceOrderProductPlanService.getUnSceduledProduceLines(queryWrapper);
        return CommonResult.success(lst);
    }

    /**
     * 获取所有的倒序未排产的产线
     *
     * @return
     */
    @ApiOperation(value = "订单产品排产-获取倒序未排产产线", notes = "订单产品排产-获取倒序未排产产线")
    @GetMapping(value = "/getUnReverseSceduledProduceLines")
    public CommonResult<?> getUnReverseSceduledProduceLines(@RequestParam(name = "produceLineNo", required = false) String produceLineNo) {
        QueryWrapper<SimpleProduceLineVo> queryWrapper = new QueryWrapper<>();
        if (StringUtil.isNotEmpty(produceLineNo)) {
            queryWrapper.like("popp.produce_Line_No", produceLineNo);
        }
        List<SimpleProduceLineVo> lst = produceOrderProductPlanService.getUnReverseSceduledProduceLines(queryWrapper);
        return CommonResult.success(lst);
    }

    /**
     * 給特定的产线排产
     *
     * @return
     */
    @ApiOperation(value = "订单产品排产-产线排产", notes = "订单产品排产-产线排产")
    @PostMapping(value = "/sceduledProduceLine")
    public CommonResult<?> sceduledProduceLine(@RequestBody ScheduleProduceLineVo scheduleProduceLineVo, Principal principal) {
//        if (bindingResult.hasErrors()) {
//            StringBuilder sb = new StringBuilder();
//            for (ObjectError error : bindingResult.getAllErrors()) {
//                sb.append(error.getDefaultMessage());
//                sb.append("\r\n");
//            }
//            String msg = StrUtil.format("请检查请求参数的合法性: {}" + sb.toString());
//            return CommonResult.failed(msg);
//        }
//        ScheduleProduceLineVo scheduleProduceLineVo = new ScheduleProduceLineVo();
//        scheduleProduceLineVo.setProduceLineId();
//        scheduleProduceLineVo.setPlanMethod(PlanMethod.FORWARD);
//        scheduleProduceLineVo.setTransferQty(1);
//        scheduleProduceLineVo.setBottleNeckCacheRatio(1);
//        scheduleProduceLineVo.setMaterialCacheQty(1);
        boolean result = false;
        //更新排产计划表
        QueryWrapper<ProduceOrderProductPlan> queryWrapper = new QueryWrapper<>();
        ModelProduceLine modelProduceLine = produceLineService.getById(scheduleProduceLineVo.getProduceLineId());
        if (modelProduceLine != null) {
            queryWrapper.eq("produce_line_no", modelProduceLine.getProduceLineNo());
        }
        // queryWrapper.eq("schedule_state", 0);
        List<ProduceOrderProductPlan> produceOrderProductPlans = produceOrderProductPlanService.getProduceOrderProductPlanList(queryWrapper);
        if (produceOrderProductPlans.size() == 0) {
            return null;
        }
        if (scheduleProduceLineVo.getPlanSort().equals(enumPlanSort.DELEVERYDATE_SORT)) {
            //按交货时间进行排序
            Collections.sort(produceOrderProductPlans, (ProduceOrderProductPlan o1, ProduceOrderProductPlan o2) ->
                    Convert.toInt(DateUtil.between(o1.getDeliveryDate(), o2.getDeliveryDate(), DateUnit.SECOND)));
        } else {
            //按优先级进行排序
            Collections.sort(produceOrderProductPlans, (ProduceOrderProductPlan o1, ProduceOrderProductPlan o2) ->
                    o1.getPriority().compareTo(o2.getPriority()));

        }
        //缓存一下排产结果
        List<ScheduleProduceLineResult> scheduleProduceLineResults = new ArrayList<>();
        /*
         * 一般来说一个生产工单下发到一个产线, 这样可以对产线进行排产。如果多个工单下发到同一个产线,则按照交货期的早晚采用串行排产方式
         * */
        if (scheduleProduceLineVo.getPlanMethod().equals(PlanMethod.FORWARD)) {
            for (int i = 0; i < produceOrderProductPlans.size(); i++) {

                ProduceOrderProductPlan p = produceOrderProductPlans.get(i);
                scheduleProduceLineVo.setQty(p.getPlanAmount());       //生产数量
                scheduleProduceLineVo.setDeliveryDate(p.getDeliveryDate()); //交期
                //如果计算开始时间没有定义,抛出异常
                //for test
                if (i == 0) {
                    scheduleProduceLineVo.setCalculateStartDate(DateUtil.tomorrow());
                }
                if (scheduleProduceLineVo.getCalculateStartDate() == null) {
                    throw new ApiException("请先定义排期开始时间!");
                }
                //排产
                ScheduleProduceLineResult tmpResult = produceOrderProductPlanService.calculateProduceScedule(scheduleProduceLineVo);
                if (tmpResult == null) {
                    throw new ApiException("排产失败!");
                }
                //正排的计划结束时间
                scheduleProduceLineVo.setCalculateStartDate(tmpResult.getForwardPlanEndDate());

                //缓存结果
                scheduleProduceLineResults.add(tmpResult);

                //更新排产对象的计划时间
                p.setPlanProduceStartDate(tmpResult.getCalculateStartDate());
                p.setPlanProduceEndDate(tmpResult.getForwardPlanEndDate());

                //更新工期
                p.setDuration(tmpResult.getPromiseProduceTime());//设置为极限工期还是承诺交货工期?
                //设置状态
                p.setScheduleState(tmpResult.getScheduleState());
            }
        }
        if (scheduleProduceLineVo.getPlanMethod().equals(PlanMethod.BACKWARD)) {
            for (int i = produceOrderProductPlans.size() - 1; i >= 0; i--) {

                ProduceOrderProductPlan p = produceOrderProductPlans.get(i);
                scheduleProduceLineVo.setQty(p.getPlanAmount());       //生产数量
                scheduleProduceLineVo.setDeliveryDate(p.getDeliveryDate()); //交期

                if (i == produceOrderProductPlans.size() - 1) {
                    scheduleProduceLineVo.setCalculateStartDate(p.getDeliveryDate());
                }
                if (scheduleProduceLineVo.getCalculateStartDate() == null) {
                    throw new ApiException("请先定义排期开始时间!");
                }
                //排产
                ScheduleProduceLineResult tmpResult = produceOrderProductPlanService.calculateProduceScedule(scheduleProduceLineVo);
                if (tmpResult == null) {
                    throw new ApiException("排产失败!");
                }
                //倒排计划开始时间
                scheduleProduceLineVo.setCalculateStartDate(tmpResult.getBackwardPlanStartDate());

                //缓存结果
                scheduleProduceLineResults.add(tmpResult);

                //更新排产对象的计划时间
                p.setLatestProduceStartDate(tmpResult.getBackwardPlanStartDate());
                p.setLatestProduceEndDate(tmpResult.getCalculateStartDate());

                //更新工期
                p.setDuration(tmpResult.getPromiseProduceTime());//设置为极限工期还是承诺交货工期?
                //设置状态
                p.setScheduleState(tmpResult.getScheduleState());
            }
        }
        //批量更新数据库
        if (scheduleProduceLineResults.size() > 0) {
            result = produceOrderProductPlanService.updateBatchById(produceOrderProductPlans);
        }

        //初始化日报工数据，这里应该是和mes的接口
        initProduceDayReports(scheduleProduceLineVo, produceOrderProductPlans);

        //初始化物料需求表，这应该是和ERP、WMS对接
        initMaterialRequire(scheduleProduceLineVo,produceOrderProductPlans);

        //发送邮件给相关人员(当前用户)
        UmsAdmin umsAdmin = adminService.getAdminByUsername(principal.getName());
        umsAdmin.setEmail("40475841@qq.com");
//        if(umsAdmin != null){
//            (new Thread(new MailUtils(umsAdmin.getEmail(),
//                "订单号：" + scheduleProduceLineVo.getOrderNo() + "\n" +
//                        "产品：" + scheduleProduceLineVo.getProductNo() + "\n" +
//                        "产线：" + scheduleProduceLineVo.getProduceLineId() + "\n" +
//                        "排序方式：" + scheduleProduceLineVo.getPlanMethod().getMsg() + "\n" +
//                        "状态：排产成功!", EmailTypeEnum.SCHEDULE.getType()))).start();
//        }
        return result ? CommonResult.success("排产成功!") : CommonResult.failed("排产失败!");
    }

    private void initProduceDayReports(ScheduleProduceLineVo scheduleProduceLineVo, List<ProduceOrderProductPlan> produceOrderProductPlans) {
        QueryWrapper<ProduceOrderProductPlan> queryWrapper = new QueryWrapper<>();
        List<MesProcedure> procedures = produceOrderProductPlanService.getProcedures(scheduleProduceLineVo.getProduceLineId());
        for (MesProcedure itm : procedures
        ) {
            ProduceDayReport produceDayReport = new ProduceDayReport();
            for (ProduceOrderProductPlan item : produceOrderProductPlans
            ) {
                produceDayReport.setOrderNo(item.getOrderNo());
                produceDayReport.setProductNo(item.getProductNo());
                produceDayReport.setProcedureNo(itm.getProcedureNo());
                QueryWrapper<PMProduct> queryWrapper1 = new QueryWrapper<PMProduct>();
                queryWrapper1.eq("no", item.getProductNo());
                PMProduct pmProduct = productService.getOne(queryWrapper1);
                if (null != pmProduct) {
                    produceDayReport.setProductName(pmProduct.getName());
                }
                produceDayReport.setProcedureName(itm.getProcedureName());
                produceDayReport.setBatch(item.getBatch());
                produceDayReport.setTotalAmount(item.getTotalAmount());
                produceDayReport.setProducedAmount(0);
                produceDayReport.setPlanAmount(item.getPlanAmount());
                produceDayReport.setLastReportDate(new Date());
                produceDayReportService.saveDayReport(produceDayReport);
            }
        }
    }


    private void initMaterialRequire(ScheduleProduceLineVo scheduleProduceLineVo, List<ProduceOrderProductPlan> produceOrderProductPlans) {
        QueryWrapper<sdMaterialRequire> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("schedule_period",produceOrderProductPlans.get(0).getSchedulePeriod());
        List<sdMaterialRequire> sdMaterialRequires = sdMaterialRequireService.list(queryWrapper);
        if( null == sdMaterialRequires || sdMaterialRequires.size() == 0) {
            DayReportDetailConditionDto dayReportDetailConditionDto = new DayReportDetailConditionDto();
            dayReportDetailConditionDto.setSchedulePeriod(produceOrderProductPlans.get(0).getSchedulePeriod());
            List<sdMaterialRequire> materialRequiredVos = produceOrderProductPlanService.getRequiredMaterial(dayReportDetailConditionDto);
            sdMaterialRequireService.saveBatch(materialRequiredVos);
        }
    }

    /**
     * 給特定的产线的所有工序
     *
     * @param produceLineId
     * @return
     */
    @ApiOperation(value = "订单产品排产-产线排产", notes = "订单产品排产-产线排产")
    @GetMapping(value = "/getProcedures")
    public CommonResult<?> getProcedures(@RequestParam(name = "produceLineId", required = true, defaultValue = "0") Long produceLineId) {

        List<MesProcedure> result = produceOrderProductPlanService.getProcedures(produceLineId);
        return CommonResult.success(result);
    }

    /**
     * 获取所有排产线所有瓶颈工序
     *
     * @param produceLineBottleneckDto
     * @return
     */
    @ApiOperation(value = "订单产品排产-产线排产", notes = "订单产品排产-获取所有排产线所有瓶颈工序")
    @PostMapping(value = "/getBottleneckProcedure")
    public CommonResult<?> getBottleneckProcedure(@RequestBody ProduceLineBottleneckDto produceLineBottleneckDto) {
        List<MesProcedure> result = mesProductProcedureBranchProcedureService.getBottleneckProcedure(produceLineBottleneckDto);
        return CommonResult.success(result);
    }

    /**
     * 获取某月所有的甘特图数据
     *
     * @param monthGanttDto
     * @return
     */
    @ApiOperation(value = "订单产品排产-获取某月所有的甘特图数据", notes = "订单产品排产-获取某月所有的甘特图数据")
    @PostMapping(value = "/getMonthGanttTask")
    public CommonResult<?> getMonthGanttTask(@RequestBody MonthGanttDto monthGanttDto) {
        MonthGantTask result = produceOrderProductPlanService.getMonthGanttTask(monthGanttDto);
        return CommonResult.success(result);
    }

    /**
     * 获取某月的物料需求
     *
     * @param dayReportDetailConditionDto
     * @return
     */
    @ApiOperation(value = "订单产品排产-获取某月的物料需求", notes = "订单产品排产-获取某月的物料需求")
    @GetMapping(value = "/getRequiredMaterial")
    public CommonResult<?> getRequiredMaterial( DayReportDetailConditionDto dayReportDetailConditionDto,
                                                @RequestParam(name = "page", required = false, defaultValue = "1") Integer page,
                                                @RequestParam(name = "size",required = false, defaultValue = "6") Integer size
                                                ) {
        Page<MaterialRequiredVo> MaterialRequiredVos = new Page<MaterialRequiredVo>(page, size);
        IPage<sdMaterialRequire> result = produceOrderProductPlanService.getRequiredMaterial(MaterialRequiredVos,dayReportDetailConditionDto);
        return CommonResult.success(result);
    }


    /**
     * 获取所有的排产的产线
     *
     * @return
     */
    @ApiOperation(value = "订单产品排产-获取排产产线", notes = "订单产品排产-获取排产产线")
    @GetMapping(value = "/getSceduledProduceLines")
    public CommonResult<?> getSceduledProduceLines(@RequestParam(name = "produceLineNo", required = false, defaultValue = "") String produceLineNo) {
        QueryWrapper<SimpleProduceLineVo> queryWrapper = new QueryWrapper<>();
        if (StringUtil.isNotEmpty(produceLineNo)) {
            queryWrapper.like("popp.produce_Line_No", produceLineNo);
        }
        List<SimpleProduceLineVo> lst = produceOrderProductPlanService.getSceduledProduceLines(queryWrapper);
        return CommonResult.success(lst);
    }

    /**
     * 分页列表查询已排产计划
     *
     * @param page
     * @param size
     * @param req
     * @return
     */
    @ApiOperation(value = "订单产品排产-分页列表已排产计划", notes = "订单产品排产-分页列表已排产计划")
    @GetMapping(value = "/getScheduledProduceOrderProductPlans")
    public CommonResult<?> getScheduledProduceOrderProductPlans(
            @RequestParam(name = "produceLineId", required = false,defaultValue = "0") Long produceLineId,
            @RequestParam(name = "planPeriod", required = false,defaultValue = "") String planPeriod,
            @RequestParam(name = "page", required = false,defaultValue = "1") Integer page,
            @RequestParam(name = "size", required = false,defaultValue = "10") Integer size,
            HttpServletRequest req) {
        QueryWrapper<ProduceOrderProductPlan> queryWrapper = new QueryWrapper<>();
        if (produceLineId > 0) {
            queryWrapper.eq("produce_line_id", produceLineId);
        }
        if(StrUtil.isNotBlank(planPeriod)){
            queryWrapper.eq("schedule_period", planPeriod);
        }
        Page<ProduceOrderProductPlan> pageProduceOrderProductPlan = new Page<ProduceOrderProductPlan>(page, size);
        IPage<ProduceOrderProductPlan> pageList = produceOrderProductPlanService.getScheduledProduceOrderProductPlans(pageProduceOrderProductPlan, queryWrapper);
        return CommonResult.success(pageList);
    }


}

